瓦片预缓存 Sample详情

最后更新时间:2019年7月5日

功能介绍

在线地图服务是移动终端常用的出图方式,在线地图服务通常是以瓦片为出图单元,最终将瓦片按照一定的规则拼接出图。为了给用户提供流畅、高效、快速的地图浏览体验,MapGIS Mobile不仅采用了高性能的地图渲染引擎,还提供了自动缓存的策略。在加载在线瓦片地图时,系统会自动将浏览过的范围、级别的地图进行自动缓存,存储到移动设备中,再次显示该区域的地图时直接读取离线本地数据,不用请求在线服务,从而使得地图能够非常迅速地渲染展示。这是MapGIS Mobile提供的自动缓存策略,存在一定的局限性,对于没有浏览过的区域的地图不会进行缓存。这无法满足要求在断网情况下同样能够浏览任意级别、任意区域地图的场景。所以MapGIS Mobile提供了瓦片预缓存的功能。

为了提高在线瓦片地图的加载速度,地图瓦片可以在终端预生成本地缓存,客户端浏览地图时可直接读取,这种技术被称作为瓦片预缓存技术。对于移动端来说,受限于硬件水平、网络速度、存储空间等因素,实现与PC终端相同的地图浏览及交互体验却相当困难,瓦片预缓存技术便在一定程度上解决了上述问题;另一方面,瓦片预缓存在在线浏览地图时动态缓存到本地存储,当在离线的时候亦可以浏览相同地图,这在野外作业中尤为重要。

瓦片预缓存实现的原理:在网络通畅情况下,根据用户需求,按照范围、级别将在线地图瓦片在终端生成本地缓存瓦片,存储到本地数据库中。从而在网络未连接的情况下,客户端直接读取本地缓存瓦片,同样能够进行地图的展示浏览。

MapGIS Mobile支持多种来源的在线地图,其中能够使用瓦片预缓存功能的数据类型包括:MapGIS IGServer在线瓦片地图、WMS地图服务、WMTS地图服务、天地图、Google地图、百度地图、高德地图、OSM地图、Bing地图等。

在缓存时,可以设置地图的级别范围、地图范围来实现更精确、更有针对性的瓦片缓存。

功能接口

瓦片预缓存功能对应的核心类为服务图层类MGSServerLayer,核心接口如下所示:

接口 说明
setCacheLocation: 设置缓存路径
setTilePreFetchListener: 设置预缓存状态监听
preFetchWithMinZoom:maxZoom:rect: 预缓存
stopFetchWithTaskID: 停止预缓存
clearCache 清除缓存

实现方法

瓦片预缓存的大体实现方法如下所示:

(1)加载地图:首先需要加载能够进行瓦片预缓存功能的在线地图,并获取待进行缓存的MGSServerLayer图层对象;

(2)准备预缓存条件:选择需要缓存的地图范围、瓦片级别范围;

(3)设置监听:利用setTilePreFetchListener接口监听瓦片缓存的进度、状态;

(4)开始缓存:调用preFetch方法开始缓存;

(5)显示缓存:在缓存完成之后,可调用缓存进行显示。

实现过程

1

加载地图

加载在线瓦片地图,以IGServer瓦片地图服务为例。

//创建并初始化服务图层
MGSMapServer* server = [MGSServerLayer createMapServerByType:MAPSERVER_TYPE_IGSERVER_TILE];
[server setUrl:@"http://develop.smaryun.com:6163/igs/rest/mrms/tile/JWWORLDTILE"];
[server setName:@"IGServer瓦片服务图层"];
//服务图层
MGSServerLayer *serverLayer=[[MGSServerLayer alloc] init];
//设置瓦片预缓存路径
[serverLayer setCacheLocation:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"/MapGIS Mobile 2D Sample/TileCache/"];];
//设置只从服务器中访问
[serverLayer setAccessMode:ModeServerOnly];
[serverLayer setMapServer:server];
//创建地图对象
MGSMap *map=[[MGSMap alloc] init];
//给地图添加服务图层
[map append:serverLayer];
//为地图容器设置地图
[_mapView setMapAsync:map callback:^(BOOL success) {
    if (success) {
        //地图加载成功
    } else {
        //地图加载失败
    }
}];

代码说明:(1)进行瓦片预缓存的MGSServerLayer服务图层,需定义为成员变量。(2)设置缓存路径setCacheLocation必须要在调用setMapServer之前执行,因为调用setMapServer的时候就会自动创建缓存了。

2

准备预缓存条件

在进行瓦片缓存时,需要设置缓存的地图范围,由于瓦片地图采用了分级显示的机制,所以进行缓存时,也需要选择缓存的瓦片级别。在缓存时将会只缓存此地图范围内设置的级别的地图。设置的地图范围越小,缓存消耗时间越少,缓存后的文件数据量越小。选择的瓦片级别范围越多,消耗时间越多。级别越大的瓦片,缓存时消耗的时间越多。根据实际需求选择合理的地图范围、瓦片级别范围。

MGSMapServer *mapServer=[serverLayer mapServer];
//连接数据(真实的连接数据源,可以获取服务返回相关信息,如瓦片信息)
long connect = [mapServer connectData];
if (connect == 1) {
    if ([mapServer isKindOfClass:[MGSTileMapServer class]]) {
        //转换为瓦片服务,获取最小最大级别
        MGSTileMapServer *tileMapServer=(MGSTileMapServer *)mapServer;
        long serverMinZoom = [tileMapServer minZoom];
        long serverMaxZoom = [tileMapServer maxZoom];
    }
}

3

设置缓存状态监听

在缓存之前,调用setTilePreFetchListener接口为服务图层设置缓存监听器,在其回调函数中可获取缓存的状态、进度、结果信息。需注意的是这些回调函数都是在子线程中执行的,所以如果需要讲缓存进度信息展示在视图中,需在主线程中处理。

可以创建瓦片预缓存监听类,继承MGSTilePreFetchListener,实现其中的方法。

#import <MapGIS_Mobile_Map/map/MGSServerLayer.h>

#import "TilePreFetch_ViewController.h"

@interface MyTilePreFetchListener : MGSTilePreFetchListener

@end

具体方法实现:

//实例化监听器
MyTilePreFetchListener *customTilePreFetchListener=[[MyTilePreFetchListener alloc] init];
//设置预缓存监听
[serverLayer setTilePreFetchListener:customTilePreFetchListener];

/*!
 @brief 预缓存进度监听
 @param taskID 任务ID,参见:ServerLayer.preFetch()
 @param totalTileCount 待预缓存的瓦片总数
 @param curTileIndex 当前缓存的瓦片索引,从0开始
 @param curTileFetchProgress 当前缓存进度[0,100]
 */
-(void)onProgressingWithTaskID:(int)taskID totalTileCount:(int)totalTileCount curTileIndex:(int)curTileIndex curTileFetchProgress:(double)curTileFetchProgress{
    NSString *progress=[NSString stringWithFormat:@"任务ID:%d\n瓦片总数:%d\n当前瓦片索引:%d\n当前缓存进度:%.2f%%",taskID,totalTileCount,curTileIndex,curTileFetchProgress];
}

/*!
 @brief 缓存状态监听
 @param taskID 任务ID,参见:ServerLayer.preFetch()
 @param zoom 当前缓存瓦片的级别
 @param row 当前缓存瓦片的行号
 @param col 当前缓存瓦片的列号
 @param status 前缓存瓦片的状态:0:成功,1:获取瓦片失败,2:写入失败.
 */
-(void)onTileFetchedWithTaskID:(int)taskID zoom:(int)zoom row:(int)row col:(int)col status:(short)status{
    NSLog(@"预缓存状态监听:任务ID:%d,当前缓存瓦片级别:%d,当前缓存瓦片行号:%d,当前缓存瓦片列号:%d,当前缓存瓦片状态:%d",taskID,zoom,row,col,status);
    if (status == 0) {
        s=[s stringByAppendingString:@"当前缓存瓦片的状态:成功"];
    } else if (status == 1){
        s=[s stringByAppendingString:@"当前缓存瓦片的状态:获取瓦片失败"];
    } else if (status == 2){
        s=[s stringByAppendingString:@"当前缓存瓦片的状态:写入失败"];
    }
    NSLog(@"%@",s);
}

/*!
 @brief 预缓存进度监听
 @param taskID 任务ID,参见:ServerLayer.preFetch()
 @param finishStatus 0:正常结束,1:非正常结束
 */
-(void)onFetchFinishWithTaskID:(int)taskID finishStatus:(short)finishStatus{
    NSLog(@"预缓存结束监听:任务ID:%d,缓存结果:%d",taskID,finishStatus);
    if (finishStatus == 0) {
        NSLog(@"下载完成");
    } else if (finishStatus == 1) {
        NSLog(@"下载失败");
    }
}

可利用此方法从子线程回到主线程,从而更新UI。

dispatch_async(dispatch_get_main_queue(), ^{
    //回到主线程
});

4

开始缓存

准备工作完成后,可调用preFetch方法,指定缓存的最小最大级别、地图空间范围开始缓存,返回值为缓存的任务ID。缓存的级别可从第0级到第0级,下载的即为第0级的瓦片。在缓存时,需要确保网络状态良好,避免造成缓存失败的情况。

//预缓存(参数:预缓存的最小级别,预缓存的最大级别,预缓存的空间范围)(异步方法),返回任务ID,可支持多任务并发
int taskID = [serverLayer preFetchWithMinZoom:selectedStartZoom maxZoom:selectedEndZoom rect:selectedRect];

5

显示缓存

根据缓存监听可判断缓存结果,缓存成功完成后,会在缓存路径中生成一个“**.mtdb”缓存文件。缓存完成后,可调用离线缓存,在未连接网络时同样能够显示地图。

在调用缓存显示地图时,与普通的通过在线请求显示地图方法类似,另外需调用setAccessMode方法设置从缓存中读取数据,并调用setCacheLocation方法设置缓存数据存在的路径,关键代码如下所示:

//创建并初始化服务图层
MGSMapServer* server = [MGSServerLayer createMapServerByType:MAPSERVER_TYPE_IGSERVER_TILE];
[server setUrl:@"http://develop.smaryun.com:6163/igs/rest/mrms/tile/JWWORLDTILE"];
[server setName:@"IGServer瓦片服务图层"];
//服务图层
MGSServerLayer *serverLayer=[[MGSServerLayer alloc] init];
//设置瓦片预缓存路径
[serverLayer setCacheLocation:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"/MapGIS Mobile 2D Sample/TileCache/"];];
//设置只从缓存中读取数据
[serverLayer setAccessMode:ModeCacheOnly];
[serverLayer setMapServer:server];
//创建地图对象
MGSMap *map=[[MGSMap alloc] init];
//给地图添加服务图层
[map append:serverLayer];
//为地图容器设置地图
[_mapView setMapAsync:map callback:^(BOOL success) {
    if (success) {
        //地图加载成功
    } else {
        //地图加载失败
    }
}];